﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Blade {
	public sealed class ServiceContainer : IServiceContainer {
		class ServiceEntry {
			public ServiceType Kind;
			public Func<IServiceContainer, object> Callback;
			public object Instance;
			public Type AliasedTo;
		}

		Dictionary<Type, ServiceEntry> entries = new Dictionary<Type, ServiceEntry>();

		public void AddService<T>(Func<IServiceContainer, T> callback, ServiceType kind) {
			ServiceEntry entry;
			if (entries.TryGetValue(typeof(T), out entry)) {
				if (entry.Kind < kind)
					return;
			}
			entries[typeof(T)] = new ServiceEntry {
				Kind = kind,
				Callback = (c) => callback(c)
			};
		}
		public void AddServiceAlias<TService, TAlias>(ServiceType kind) where TAlias : TService {
			ServiceEntry entry;
			if (entries.TryGetValue(typeof(TService), out entry)) {
				if (entry.Kind < kind)
					return;
			}
			entries[typeof(TService)] = new ServiceEntry {
				Kind = kind,
				AliasedTo = typeof(TAlias)
			};
		}
		public void RemoveService<T>() {
			entries.Remove(typeof(T));
		}
		public bool ContainsService<T>() {
			ServiceEntry entry;
			return TryFindEntry(typeof(T), out entry);
		}
		public bool TryGetService<T>(out T value) {
			ServiceEntry entry;
			if (TryFindEntry(typeof(T), out entry)) {
				if (entry.Instance == null)
					entry.Instance = entry.Callback(this);
				value = (T)entry.Instance;
				return true;
			}
			value = default(T);
			return false;
		}
		private bool TryFindEntry(Type type, out ServiceEntry entry) {
			while (type != null && entries.TryGetValue(type, out entry)) {
				if (entry.Callback == null && entry.Instance == null && entry.AliasedTo != null) {
					type = entry.AliasedTo;
					continue;
				}
				return true;
			}
			entry = default(ServiceEntry);
			return false;
		}
	}
}
